package com.imooc.security.server;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import com.imooc.security.core.properties.Oauth2ClientProperties;
import com.imooc.security.core.properties.SecurityProperties;

/**
 * 6.2.5 app授权服务器配置
 * 加了这个等于把app定义成认证服务器。可以通过app获取token
 */
@Configuration
@EnableAuthorizationServer
/**
 * 6.2.6 测试访问：localhost:8060/oauth/authorize?response_type=code&client_id=30f54f19-d5e7-4d7e-a4fb-c4bbb9c33302&redirect_uri=http://example.com&scope=all
 * 启动项目，发现默认多了/oauth/authorize, /oauth/token这些url。
 * 控制台打印:security.oauth2.client.clientId = 30f54f19-d5e7-4d7e-a4fb-c4bbb9c33302 security.oauth2.client.secret = 6fba89e7-1961-4dcb-ad3d-de74ceeacba0
 * 输入的用户名密码（test,123456）：哪个用户在请求授权
 * client_id：哪个应用在请求授权
 * scope：给你什么授权
 * 
 * 6.2.9 测试访问：localhost:8060/oauth/authorize?response_type=code&client_id=imooc&redirect_uri=http://example.com&scope=all
 * code：example.com/?code=XfQ8SU
 * 
 * 【授权码模式】
 * 使用restlet client模拟第三方应用获取token
 * POST http://127.0.0.1:8060/oauth/token
 * HEADERS: 
 * Authorization : Basic aW1vb2M6aW1vb2NzZWNyZXQ= [ Basic Username imooc Password imoocsecret ]
 * Content-Type : application/x-www-form-urlencoded
 * BODY Form:
 * grant_type[Text]=authorization_code
 * client_id[Text]=imooc
 * code[Text]=XfQ8SU
 * redirect_uri[Text]=http://example.com
 * scope[Text]=all
 * 
 * 【密码模式】
 * 使用restlet client模拟第三方应用获取token
 * POST http://127.0.0.1:8060/oauth/token
 * HEADERS: 
 * Authorization : Basic aW1vb2M6aW1vb2NzZWNyZXQ= [ Basic Username imooc Password imoocsecret ]
 * Content-Type : application/x-www-form-urlencoded
 * BODY Form:
 * grant_type[Text]=password
 * username[Text]=ndood
 * password[Text]=123456
 * scope[Text]=all
 * 同一个用户，不同方式获取多次token，得到的是同一个
 */
/**
 * 6.3.1 springOauth源码解析
 * 先用restlet client模拟密码模式获取token的请求
 * TokenEndpoint 95
 * TokenEndpoint 132
 * CompositeTokenGranter 37
 * AbstractTokenGranter 59
 * AbstractTokenGranter 70
 * AbstractTokenGranter 74
 * ResourceOwnerPasswordTokenGranter 62
 * 重新用restlet client模拟密码模式获取token的请求
 * DefaultTokenServices 84
 */
/**
 * 6.8.4 测试访问密码模式
 * 【密码模式】
 * 使用restlet client模拟第三方应用获取token
 * POST http://127.0.0.1:8060/oauth/token
 * HEADERS: 
 * Authorization : Basic aW1vb2M6aW1vb2NzZWNyZXQ= [ Basic Username imooc Password imoocsecret ]
 * Content-Type : application/x-www-form-urlencoded
 * BODY Form:
 * grant_type[Text]=password
 * username[Text]=ndood
 * password[Text]=123456
 * scope[Text]=all
 * 成功获取到token xxxxxxxxxxxxxxxx
 * 
 * 使用令牌访问服务
 * GET http://127.0.0.1:8060/user/me
 * HEADERS: 
 * Authorization : bearer xxxxxxxxxxxxxxxx
 * 重启服务访问资源，发现旧的token失效
 */
/**
 * 6.8.13 重新测试密码模式获取token
 * redis keys * 发现多了token持久化数据
 * 重启服务访问资源，发现旧的token仍然有效
 */
/**
 * 6.9.8 测试访问密码模式
 * 【密码模式】
 * 使用restlet client模拟第三方应用获取token
 * POST http://127.0.0.1:8060/oauth/token
 * HEADERS: 
 * Authorization : Basic aW1vb2M6aW1vb2NzZWNyZXQ= [ Basic Username imooc Password imoocsecret ]
 * Content-Type : application/x-www-form-urlencoded
 * BODY Form:
 * grant_type[Text]=password
 * username[Text]=ndood
 * password[Text]=123456
 * scope[Text]=all
 * 成功获取到jwt token yyyyyyyyyyyyyyyy
 * 
 * 6.9.9 解析jwt jsonwebtoken.io
 * 使用jwt令牌访问服务
 * GET http://127.0.0.1:8060/user/me
 * HEADERS: 
 * Authorization : bearer yyyyyyyyyyyyyyyy
 * 发现返回null，因为用了jwt，此时的authenticationPrincipal是一个字符串，而不是UserDetails
 * 
 * 6.9.11 修改/user/me后重新测试
 * 使用jwt令牌访问服务
 * GET http://127.0.0.1:8060/user/me
 * HEADERS: 
 * Authorization : bearer yyyyyyyyyyyyyyyy
 * 发现返回authentication内容
 * 
 * 6.9.17 测试token附加信息
 * 先获取jwt token，再用jwt token请求/user/me，发现多了company
 * 
 * 6.9.20 测试刷新jwt token
 * POST http://127.0.0.1:8060/oauth/token
 * BODY Form:
 * grant_type : refresh_token
 * refresh_token : yyyyyyyyyyyyyyyy
 * scope : all
 * 成功获取到新的jwt token
 * 也可以设置refresh_token有效期，最好设置长一点
 */
// 6.8.1 继承AuthorizationServerConfigurerAdapter
public class ImoocAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{
	
	@Autowired
	private AuthenticationManager authenticationManager;
	
	@Autowired
	private UserDetailsService userDetailsService;
	
	@Autowired
	private SecurityProperties securityProperties;
	
	@Autowired
	private TokenStore tokenStore;
	
	@Autowired(required = false)
	private JwtAccessTokenConverter jwtAccessTokenConverter;
	
	@Autowired(required = false)
	private TokenEnhancer jwtTokenEnhancer;
	
	/**
	 * 6.8.2 修改endpoints配置
	 * TokenEndpoint是用来处理/oauth/token请求的入口
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		/*endpoints.authenticationManager(authenticationManager)
		.userDetailsService(userDetailsService);*/
		// 6.8.12 改造成redis持久化模式
		endpoints.tokenStore(tokenStore)
			.authenticationManager(authenticationManager)
			.userDetailsService(userDetailsService);
		
		// 6.9.7 配置jwtTokenConverter，不光jwtTokenStore需要，其它地方也需要
		/*if(jwtAccessTokenConverter !=null){
			endpoints.accessTokenConverter(jwtAccessTokenConverter);
		}*/
		// 6.9.16 配置jwtTokenEnhancer  DefaultTokenServices 301
		if(jwtAccessTokenConverter !=null && jwtTokenEnhancer!=null){
			TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
			List<TokenEnhancer> enhancers = new ArrayList<TokenEnhancer>();
			enhancers.add(jwtTokenEnhancer);
			enhancers.add(jwtAccessTokenConverter);
			enhancerChain.setTokenEnhancers(enhancers);
			
			endpoints
				.tokenEnhancer(enhancerChain)
				.accessTokenConverter(jwtAccessTokenConverter);
		}
	}
	
	/**
	 * 6.8.3 修改跟客户端相关的一些配置
	 * 我们的认证服务器会给哪些第三方应用发这些令牌
	 * 配置的clientId clientSecret就不再起作用了：
	 * security.oauth2.client.clientId = imooc   
	 * security.oauth2.client.clientSecret = imoocsecret
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		/*clients.inMemory().withClient("imooc")
			.secret("imoocSecret")
			.accessTokenValiditySeconds(7200)// 发出令牌有效时间
			.authorizedGrantTypes("refresh_token","password") // 针对imooc这个客户所能支持的授权模式有哪些
			.scopes("all","read","write")
			.and()
			.withClient("xxx");*/
		// 6.8.9 重构成可配置的
		InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
		if(ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())){
			for(Oauth2ClientProperties config: securityProperties.getOauth2().getClients()){
				System.out.println(config.getClientId()+","+config.getClientSecret());
				builder.withClient(config.getClientId())
				.secret(config.getClientSecret())
				.accessTokenValiditySeconds(config.getAccessTokenValiditySeconds())
				.authorizedGrantTypes("refresh_token","password");
			}
		}
	}

}
